//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

s64 cfg_blanks_skip(struct cfg_ctx *c,s64 start,s64 line_end)
{
    while(1){
      if(start==line_end) break;
      u8 start_char=*(u8*)(c->m+start);
      if(start_char!=' '&&start_char!='\t') break;
      ++start;
    }
    return start;
}

s64 cfg_non_blanks_skip(struct cfg_ctx *c,s64 start,s64 line_end)
{
    while(1){
      if(start==line_end) break;
      u8 start_char=*(u8*)(c->m+start);
      if(start_char==' '||start_char=='\t') break;
      ++start;
    }
    return start;
}

s64 cfg_key_skip_to_val(struct cfg_ctx *c,s64 start,s64 line_end)
{
    while(1){
      if(start==line_end) break;
      if(*(u8*)(c->m+start)=='=') break;
      ++start;
    }
    if(start==line_end) return CFG_KEY_SKIP_TO_VAL_NO_VAL;
    s64 val_start=start+1;
    if(val_start==line_end) return CFG_KEY_SKIP_TO_VAL_NO_VAL;
    return val_start;
}

//line_end is the offset of the terminating '/n' or of the byte right after
//the last file byte
s64 cfg_line_end_reach(struct cfg_ctx *c,s64 line_start)
{
  s64 line_end=line_start;
  while(1){
    if((u64)line_end==c->sz||*(u8*)(c->m+line_end)=='\n') return line_end;
    ++line_end;
  }
}

s8 cfg_file_open_ro(struct cfg_ctx *c)
{
  i fd;
  while(1){fd=(i)open(g_cfg_path,O_RDONLY,0); if(fd!=-EINTR) break;}
  if(ISERR(fd)){
    PERR("cfg:error(%d):unable to open conf file %s\n",fd,g_cfg_path);
    goto err;
  }

  struct stat cfg_stat;
  l r=fstat(fd,&cfg_stat);
  if(ISERR(r)){
    PERR("cfg:error(%ld):unable to stat conf file %s\n",r,g_cfg_path);
    goto err_close_fd;
  }

  l addr=mmap(0,cfg_stat.sz,PROT_READ,MAP_PRIVATE|MAP_POPULATE,fd,0);
  if(!addr||ISERR(addr)){
    PERR("cfg:error(%ld):unable to mmap conf file %s\n",addr,g_cfg_path);
    goto err_close_fd;
  }

  c->fd=fd;
  c->m=(void*)addr;
  c->sz=(u64)cfg_stat.sz;

  LOG_CFG("conf file %s opened and mmaped\n",g_cfg_path);
  return LWL_OK;

err_close_fd:
  while(1){r=close(fd); if(r!=-EINTR) break;}
  if(ISERR(r))
    PERR("cfg:error(%ld):unable to close conf file\n",r,g_cfg_path);
err:
  return LWL_ERR;
}

s8 cfg_file_close(struct cfg_ctx *c)
{
  s8 r0=LWL_OK;

  l r=munmap(c->m,c->sz);
  if(ISERR(r)){
    PERR("cfg:error(%ld):unable to munmap conf file\n",g_cfg_path);
    r0=LWL_ERR;goto exit;
  }

  while(1){r=close(c->fd); if(r!=-EINTR) break;}
  if(ISERR(r)){
    PERR("cfg:error(%ld):closing conf file gone wrong\n",r,g_cfg_path);
    r0=LWL_ERR;goto exit;
  }

  LOG_CFG("conf file %s unmapped and closed\n",g_cfg_path);
exit:
  return r0;
}
